package com.n.passwordbook.Utils;

import com.n.passwordbook.PasswordBook.PasswordBook;
import com.n.passwordbook.PasswordBook.PasswordStruct;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableView;

import java.util.ArrayList;

public class OperationManager
{
    private static OperationManager instance;

    private int head;
    private int undoLimit;
    private int checkPoint;
    private TableView<PasswordStruct> passwordTableView;
    private final ArrayList<ObservableList<PasswordStruct>> snapshotsArray;

    private OperationManager()
    {
        head = 0;
        undoLimit = Settings.getUndoLimit();
        checkPoint = -1;
        snapshotsArray = new ArrayList<>(undoLimit);
    }

    public static OperationManager getInstance()
    {
        if (instance == null)
        {
            instance = new OperationManager();
        }
        return instance;
    }

    public static void reset(ObservableList<PasswordStruct> snapshot, TableView<PasswordStruct> tableView)
    {
        getInstance().head = -1;
        getInstance().snapshotsArray.clear();
        for (int i = 0; i < getInstance().undoLimit; i++)
        {
            getInstance().snapshotsArray.add(null);
        }
        getInstance().Do(snapshot);
        getInstance().checkPoint = snapshot.hashCode();
        getInstance().passwordTableView = tableView;
    }

    public static void reset(int undoLimit)
    {
        var passwords = getInstance().snapshotsArray.get(0);
        getInstance().head = 0;
        getInstance().undoLimit = undoLimit;
        getInstance().snapshotsArray.clear();
        getInstance().snapshotsArray.add(passwords);
        getInstance().checkPoint = passwords.hashCode();
        for (int i = 1; i < undoLimit; i++)
        {
            getInstance().snapshotsArray.add(null);
        }
    }

    public boolean Do(ObservableList<PasswordStruct> snapshot)
    {
        if (head < undoLimit - 2)
        {
            head++;
            snapshotsArray.set(head, FXCollections.observableArrayList(snapshot));
            snapshotsArray.set(head + 1, null);
        }
        else if (head < undoLimit - 1)
        {
            head++;
            snapshotsArray.set(head, FXCollections.observableArrayList(snapshot));
        }
        else if (head == undoLimit - 1)
        {
            snapshotsArray.remove(0);
            snapshotsArray.add(FXCollections.observableArrayList(snapshot));
        }
        //DebugShow();
        return false;
    }

    private void debugShower()
    {
        for (int i = 0; i < snapshotsArray.size(); i++)
        {
            var tb = snapshotsArray.get(i);
            if (head == i)
            {
                System.out.print("->[");
            }
            else
            {
                System.out.print("  [");
            }
            if (tb != null)
            {
                for (var p : tb)
                {
                    System.out.print(p.getName() + ", ");
                }
            }
            else
            {
                System.out.print("null, ");
            }
            System.out.print("]" + (tb == null ? "null" : tb.hashCode()) + "\n");
        }
        System.out.print('\n');
    }

    public boolean undo()
    {
        if (--head < 0)
        {
            ++head;
            return isPasswordBookSame();
        }
        changeTableViewContent();
        return isPasswordBookSame();
        //DebugShow();
    }

    public boolean redo()
    {
        if (++head > undoLimit - 1)
        {
            --head;
            return isPasswordBookSame();
        }
        if (snapshotsArray.get(head) == null)
        {
            --head;
            return isPasswordBookSame();
        }
        changeTableViewContent();
        //DebugShow();
        return isPasswordBookSame();
    }

    public void saveSuccessfully()
    {
        checkPoint = snapshotsArray.get(head).hashCode();
    }

    private void changeTableViewContent()
    {
        PasswordBook.Passwords = FXCollections.observableArrayList(snapshotsArray.get(head));
        passwordTableView.setItems(PasswordBook.Passwords);
        PasswordBook.resetTableView(passwordTableView);
    }

    private boolean isPasswordBookSame()
    {
        return snapshotsArray.get(head).hashCode() == checkPoint;
    }
}